from django.shortcuts import render, reverse
from django.views.generic.base import View
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import gettext as _
from django.utils import timezone as django_timezone
from django.core.exceptions import ValidationError
from rest_framework.decorators import api_view

from core import errors
from apps.app_auth_provider.models import (
    InternalUser,
    AuthProviderType,
    ExternalUser,
    AuthRecord,
    EmailVerificationCode,
)
from apps.app_auth_provider.managers import (
    AuthProviderManager,
    AuthRecordManager,
    InternalUserManager,
    ExternalUserManager,
    AuthCodeManager,
)
from apps.app_auth_provider.providers import build_auth_provider, BaseProvider, ProviderUserInfo
from apps.app_auth_provider.forms import EmailBindForm


def render_error_response(request, message: str) -> HttpResponse:
    return render(
        request=request,
        template_name='auth_error.html',
        context={
            'message': message
        }
    )


@api_view(['GET'])
def auth_callback_view(request, *arge, **kwargs):
    """
    本服务认证回调地址。第三方认证完成后跳转回本视图。
    """
    code = request.query_params.get("code")
    record_id = request.query_params.get("state")
    provider_type = kwargs['provider_type']

    if not code:
        return render_error_response(request=request, message=_('登录回调URL错误！缺少必要的授权码:code'))

    if not record_id:
        return render_error_response(request=request, message=_('登录回调URL错误！缺少必要的参数:state'))

    try:
        auth_provider = AuthProviderManager.get_auth_provider(provider_type=provider_type)
        if auth_provider is None:
            raise errors.ConflictError(message=_('此认证服务未提供，请选择其他认证方式。'))

        provider = build_auth_provider(auth_provider=auth_provider)
        if provider is None:
            raise errors.InvalidArgument(message=_('未支持第三方认证服务，在第三方认证服务注册的回调地址可能有误。'))

        user_info = provider.get_user_info_by_code(code=code)

        auth_record = AuthRecordManager.get_auth_record(record_id=record_id)
        if auth_record is None:
            raise errors.InvalidArgument(message=_('未找到用户认证记录，无法获得回调地址，无法跳转。'))
    except Exception as e:
        return render_error_response(request=request, message=str(e))

    # 查询或创建外部用户记录
    external_user = ExternalUserManager.get_external_user(
        provider_type=provider_type,
        external_id=user_info.external_id
    )
    if external_user:
        # 已绑定过用户，直接回调
        if external_user.internal_user:
            return redirect_auth_code(internal_user=external_user.internal_user, auth_record=auth_record)
    else:
        external_user = ExternalUserManager.create_external_user(
            provider_type=provider_type,
            external_id=user_info.external_id,
            email=user_info.email if user_info.email else '',
            true_name=user_info.true_name if user_info.true_name else '',
            org_name=user_info.org_name if user_info.org_name else '',
            internal_user=None
        )

    # 认证记录状态更新
    auth_record.set_auth_status_authorized(
        internal_user=None,
        email=user_info.email,
        raise_exception=False
    )

    # 外部用户记录未绑定邮箱和内部用户，AAI认证并且有邮箱信息，直接创建绑定用户
    if provider_type == AuthProviderType.AAI.value and user_info.email:
        external_user = create_bind_internal_user(user_info=user_info, external_user=external_user)
        return redirect_auth_code(internal_user=external_user.internal_user, auth_record=auth_record)

    # 外部用户记录未绑定邮箱和内部用户，跳转到邮箱绑定视图
    redirect_to = reverse('auth_provider:email-bind', query={
        'external_user_id': str(external_user.id),
        'auth_record_id': str(auth_record.id),
        'provider_type': provider_type
    })
    return HttpResponseRedirect(redirect_to=redirect_to)


def redirect_auth_code(internal_user: InternalUser, auth_record: AuthRecord) -> HttpResponseRedirect:
    auth_code = AuthCodeManager.create_auth_code(internal_user=internal_user)

    redirect_to = BaseProvider.replace_query_params(
        url=auth_record.redirect_uri,
        params={
            'code': str(auth_code.id)
        }
    )

    auth_record.set_auth_status_completed(
        internal_user=internal_user,
        email=internal_user.email,
        raise_exception=False
    )
    return HttpResponseRedirect(redirect_to=redirect_to)


def create_bind_internal_user(user_info: ProviderUserInfo, external_user: ExternalUser) -> ExternalUser:
    internal_user = InternalUserManager.get_internal_user_by_email(email=user_info.email)
    if internal_user is None:
        internal_user = InternalUserManager.create_internal_user(
            email=user_info.email,
            true_name=user_info.true_name,
            org_name=user_info.org_name,
            is_active=True
        )

    external_user.internal_user = internal_user
    external_user.email = internal_user.email
    external_user.true_name = user_info.true_name
    external_user.org_name = user_info.org_name
    external_user.save(update_fields=['internal_user', 'email', 'true_name', 'org_name', 'updated_at'])

    return external_user


class EmailBindView(View):
    """
    邮件绑定视图
    """
    def get(self, request, *args, **kwargs):
        return self.render_to_response(request=request, form=EmailBindForm())

    def post(self, request, *args, **kwargs):
        external_user_id = request.GET.get('external_user_id', '')
        auth_record_id = request.GET.get('auth_record_id', '')

        form = EmailBindForm(request.POST)
        if not form.is_valid():
            return self.render_to_response(request=request, form=form)

        cleaned_data = form.cleaned_data
        email = cleaned_data['email']
        code = cleaned_data['code']
        name = cleaned_data['name']
        org_name = cleaned_data['org_name']

        veri_code = EmailVerificationCode.objects.select_related('external_user').filter(
            external_user_id=external_user_id,
            email=email, code=code
        ).order_by('-created_at').first()
        if veri_code is None:
            form.add_error(field=None, error=ValidationError(message=_('验证码无效')))
            return self.render_to_response(request=request, form=form)

        if veri_code.expired_at < django_timezone.now():
            form.add_error(field=None, error=ValidationError(message=_('验证码无效，已过期。')))
            return self.render_to_response(request=request, form=form)

        external_user = veri_code.external_user
        external_user = create_bind_internal_user(
            external_user=external_user,
            user_info=ProviderUserInfo(
                external_id=external_user.external_id,
                email=email,
                true_name=name,
                org_name=org_name
            )
        )

        # 删除验证码
        veri_code.delete()

        try:
            auth_record = AuthRecordManager.get_auth_record(record_id=auth_record_id)
            if auth_record is None:
                raise errors.InvalidArgument(message=_('未找到用户认证记录，无法获得回调地址，无法跳转。'))
        except errors.Error as exc:
            return render_error_response(request=request, message=str(exc))

        return redirect_auth_code(internal_user=external_user.internal_user, auth_record=auth_record)

    @staticmethod
    def render_to_response(request, form):
        external_user_id = request.GET.get('external_user_id', '')
        auth_record_id = request.GET.get('auth_record_id', '')
        provider_type = request.GET.get('provider_type', '')

        if not external_user_id:
            return render_error_response(request=request, message=_('缺少外部用户记录id参数。'))

        if not auth_record_id:
            return render_error_response(request=request, message=_('缺少用户认证记录id参数。'))

        context = {
            'provider_display': AuthProviderType(provider_type).label if provider_type in AuthProviderType else provider_type,
            'form': form
        }
        return render(request, 'email_bind.html', context=context)
